home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 09 - 1993 / 09.12 Dec 93 / Programmers' Challenge / ASCII85.c
Encoding:
C/C++ Source or Header  |  1993-10-12  |  6.3 KB  |  218 lines  |  [TEXT/KAHL]

  1. /*********************************************************
  2.  
  3.     Author : Clement J Goebel III
  4.     
  5.     Routine converts bianary data to ASCII85 ascii 
  6.     format for transfering data via methods that are
  7.     not bianary friendly, such as e-mail.
  8.     
  9.     Such that :
  10.       (i1 * 256^3)+(i2 * 256^2)+(i3 * 256)+i4 ==
  11.         (o1 * 85^4)+(o2 * 85^3)+(o3 * 85^2)+(o4 * 85)+o5
  12.     
  13.     The output must include one carrige return at least
  14.     every 80 characters.  This routine inserts a cr
  15.     after every 8 longs of input, which represent at
  16.     most 40 chars of output.  It could be once every
  17.     16 longs of input representing at most 80 chars of
  18.     output, but then the max line len could be 
  19.     interpereted as 81 characters.  One other oddity
  20.     of this implementation is that the output may
  21.     begin with a newline char.
  22.     
  23.     The output buffer passed to the routine needs to 
  24.     account for the probable expansion of the data.
  25.     Len(Output) = ((Len(Input)) * 5 + 3) / 4 + 2;
  26.         plus room for the newline characters.
  27.     
  28.  *********************************************************/
  29.  
  30. #define ASCII_BANG            '!'
  31. #define ASCII_Z                'z'
  32. #define ASCII_TILDE            '~'
  33. #define ASCII_GREATER_THAN    '>'
  34. #define ASCII_CR            0x0d
  35. #define INPUTS_BETWEEN_CR    0x07
  36.  
  37. #define k85                85L
  38. #define k85_2          7225L
  39. #define k85_3        614125L
  40. #define k85_4      52200625L
  41.  
  42. /*------------------------------------------------------
  43.     The processor's general purpose division is slow,
  44.     instead I use a routine that is accurate only for
  45.     results between 0 and 127. And to avoid speed
  46.     loss, which would result from calling the routine
  47.     hundreds of thousands of times, it is inserted 
  48.     inline via a macro.  Note this routine is only fast
  49.     if all of the parameters, except lDivisor,
  50.     are registers. lRemain starts as the number to
  51.     divide and ends up containing the remainder.
  52.   ------------------------------------------------------*/
  53. #define DIV_ANS_LT_128( lRemain, lDivisor, lAns, lT )     \
  54. {                                                          \
  55.     lT = (lDivisor << 6);                                  \
  56.                                                           \
  57.     if ( lRemain >= lT ) { lRemain -= lT; lAns += 0x40; } \
  58.     lT = ( lT >> 1 );                                      \
  59.     if ( lRemain >= lT ) { lRemain -= lT; lAns += 0x20; } \
  60.     lT = ( lT >> 1 );                                      \
  61.     if ( lRemain >= lT ) { lRemain -= lT; lAns += 0x10; } \
  62.     lT = ( lT >> 1 );                                      \
  63.     if ( lRemain >= lT ) { lRemain -= lT; lAns += 0x08; } \
  64.     lT = ( lT >> 1 );                                      \
  65.     if ( lRemain >= lT ) { lRemain -= lT; lAns += 0x04; } \
  66.     lT = ( lT >> 1 );                                      \
  67.     if ( lRemain >= lT ) { lRemain -= lT; lAns += 0x02; } \
  68.     lT = ( lT >> 1 );                                      \
  69.     if ( lRemain >= lT ) { lRemain -= lT; lAns += 0x01; } \
  70. }                                                                    
  71.  
  72. /*------------------------------------------------------
  73.     ASCII85Encode
  74.   ------------------------------------------------------*/
  75. void ASCII85Encode( char *pcInput,
  76.                     unsigned long ulInputBytes,
  77.                     char *pcOutput,
  78.                     unsigned long *pulOutputBytes )
  79. {
  80.     unsigned char *pcIn = (unsigned char*)pcInput;
  81.     unsigned char *pcOut = (unsigned char*)pcOutput;
  82.     unsigned long *plIn;
  83.     unsigned long ulIn;
  84.     unsigned long ulOut;
  85.     unsigned long ulInput;
  86.     unsigned long l;
  87.     unsigned long ulCRs;
  88.     unsigned char ucC;
  89.  
  90.     ulCRs = ulOut = 0;
  91.     ulIn = ulInputBytes >> 2;     /* longwords to read */
  92.     
  93.     if ( ((unsigned long) pcIn & 0x01 ) == 0 ) {        
  94. /*------------------------------------------------------
  95.                 Input data is word aligned
  96.   ------------------------------------------------------*/
  97.         plIn = (unsigned long*)pcInput;
  98.         while ( ulIn ) {
  99.             if (((ulIn--) & INPUTS_BETWEEN_CR) == 0 ) {
  100.                 *pcOut++ = ASCII_CR;
  101.                 ulCRs++;
  102.             }
  103.             if ( ! ( ulInput = *plIn++ ) ) {
  104.                 *pcOut++ = ASCII_Z;
  105.                 ulOut++;
  106.             } else {
  107.                 ucC = ASCII_BANG;
  108.                 DIV_ANS_LT_128( ulInput, k85_4, ucC, l );
  109.                 *pcOut++ = ucC;
  110.         
  111.                 ucC = ASCII_BANG;
  112.                 DIV_ANS_LT_128( ulInput, k85_3, ucC, l );
  113.                 *pcOut++ = ucC;
  114.     
  115.                 ucC = ASCII_BANG;
  116.                 DIV_ANS_LT_128( ulInput, k85_2, ucC, l );
  117.                 *pcOut++ = ucC;
  118.     
  119.                 ucC = ASCII_BANG;
  120.                 DIV_ANS_LT_128( ulInput, k85  , ucC, l );
  121.                 *pcOut++ = ucC;
  122.                 *pcOut++ = ulInput + ASCII_BANG;
  123.             }
  124.         }
  125.         pcIn = (unsigned char*)plIn;
  126.     } else {
  127. /*------------------------------------------------------
  128.             Else input data is NOT word aligned
  129.   ------------------------------------------------------*/
  130.         while ( ulIn ) { 
  131.             if (((ulIn--) & INPUTS_BETWEEN_CR) == 0 ) {
  132.                 *pcOut++ = ASCII_CR;
  133.                 ulCRs++;
  134.             }
  135.             ulInput = (((unsigned long)(*pcIn++)) << 24);
  136.             ulInput = ulInput | 
  137.                         ((*(unsigned long*)pcIn ) >> 8 );
  138.             pcIn += 3;
  139.  
  140.             if ( ! ulInput ) {
  141.                 *pcOut++ = ASCII_Z;
  142.                 ulOut++;
  143.             } else {
  144.                 ucC = ASCII_BANG;
  145.                 DIV_ANS_LT_128( ulInput, k85_4, ucC, l );
  146.                 *pcOut++ = ucC;
  147.         
  148.                 ucC = ASCII_BANG;
  149.                 DIV_ANS_LT_128( ulInput, k85_3, ucC, l );
  150.                 *pcOut++ = ucC;
  151.     
  152.                 ucC = ASCII_BANG;
  153.                 DIV_ANS_LT_128( ulInput, k85_2, ucC, l );
  154.                 *pcOut++ = ucC;
  155.     
  156.                 ucC = ASCII_BANG;
  157.                 DIV_ANS_LT_128( ulInput, k85  , ucC, l );
  158.                 *pcOut++ = ucC;
  159.                 *pcOut++ = ulInput + ASCII_BANG;
  160.             }
  161.         }
  162.     }
  163. /*------------------------------------------------------
  164.         ulOut contains number of longs that were 0 
  165.   ------------------------------------------------------*/
  166.       ulIn = ulInputBytes;
  167.     l = (ulIn >> 2) - ulOut;/*    l = # nonzero longs    */
  168.     ulOut += ( l << 2 );    /*   add l * 4             */
  169.     ulOut += ( l );            /*   one more for * 5    */
  170.     
  171.     ulOut += ulCRs;            /*  # of carrige rtns    */
  172.     ulOut++;                /* for last carrige rtn */
  173.  
  174. /*------------------------------------------------------
  175.         take care of leftover tail end bytes 
  176.   ------------------------------------------------------*/
  177.     *pcOut++ = ASCII_CR;
  178.  
  179.     ulIn = ulIn & 0x03;
  180.     if ( ulIn ) {
  181.         ulInput = ((unsigned long)(*pcIn++)) << 24;
  182.         if ( ulIn > 1 ) { 
  183.             ulInput = ulInput | ((unsigned long)
  184.                                     (*pcIn++) << 16);
  185.             if ( ulIn == 3 ) 
  186.                 ulInput = ulInput | ((unsigned long)
  187.                                     (*pcIn++) << 8);
  188.         }    
  189.         ucC = ASCII_BANG;
  190.         DIV_ANS_LT_128( ulInput, k85_4, ucC, l );
  191.         *pcOut++ = ucC;
  192.  
  193.         ucC = ASCII_BANG;
  194.         DIV_ANS_LT_128( ulInput, k85_3, ucC, l );
  195.         *pcOut++ = ucC;
  196.     
  197.         if ( ulIn > 1 ) {
  198.             ucC = ASCII_BANG;
  199.             DIV_ANS_LT_128( ulInput, k85_2, ucC, l );
  200.             *pcOut++ = ucC;
  201.  
  202.             if ( ulIn == 3 ) {
  203.                 ucC = ASCII_BANG;
  204.                 DIV_ANS_LT_128( ulInput, k85, ucC, l );
  205.                 *pcOut++ = ucC;
  206.             }
  207.         }                
  208.         ulOut += ( ulIn + 1 );
  209.     }
  210.  
  211. /*------------------------------------------------------
  212.                 write end of data marker 
  213.   ------------------------------------------------------*/    
  214.     *pcOut++ = ASCII_TILDE;
  215.     *pcOut++ = ASCII_GREATER_THAN;
  216.     *pulOutputBytes = ulOut + 2;
  217. }
  218.